﻿
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;


[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
    Format.UserDefined,
    IsInvariantToDuplicates = false,
    IsInvariantToNulls = false,
    IsInvariantToOrder = false,
    MaxByteSize = -1)]
public struct MedianAgg : IBinarySerialize 
{
    //List to hold the records;
    private List<double> lst;

    public void Init()
    {
        lst = new List<double>();
    }


    // Accumulate records
    public void Accumulate(SqlDouble rec_value)
    {
        if (!rec_value.IsNull)
        {
            lst.Add(rec_value.Value);
        }
    }

    // Merge the partially computed aggregate with this aggregate.
    // The other partial results to be merged
    public void Merge(MedianAgg Group)
    {
        this.lst.AddRange(Group.lst.ToArray());
    }

    // Called at the end of aggregation, to return the results.
    // The MedianAgg of all inputted values
    public SqlDouble Terminate()
    {
        //debug: return (SqlDouble)lst.Count;

        //special case 0 values
        if (lst.Count == 0)
            return SqlDouble.Null;

        lst.Sort();
        int index = (int)lst.Count / 2;

        if (lst.Count % 2 == 0)
        {
            return (SqlDouble)(((double)lst[index] + (double)lst[index - 1]) / 2);
        }
        else
        {
            return (SqlDouble)((double)lst[index]);
        }
    }


    #region IBinarySerialize Members

    public void Read(System.IO.BinaryReader r)
    {
        int cnt = r.ReadInt32();
        this.lst = new List<double>(cnt);
        for(int i = 0; i < cnt; i++)
        {
            this.lst.Add(r.ReadDouble());
        }
    }

    public void Write(System.IO.BinaryWriter w)
    {
        w.Write(this.lst.Count);
        foreach (double d in this.lst)
        {
            w.Write(d);
        }
    }

    #endregion
}